﻿using System;
using System.Collections.Generic;
using System.Linq;
using Smarly.Idml.RelaxNg.Generator.Model;
using Tenuto;
using Tenuto.Grammar;
using Tenuto.Reader;
using org.relaxng.datatype;

namespace Smarly.Idml.RelaxNg.Generator.Proxy
{
	public class BuilderProxy
	{
		private Tenuto.Grammar.Grammar _grammar;

		public BuilderProxy(string rngFile)
		{
			_grammar = new GrammarReader(new ConsoleController()).parse(rngFile);
			CreateModel();
		}

		public Dictionary<string, IStruct> Structs = new Dictionary<string, IStruct>();

		private Dictionary<string, List<IObjectName>> WaitName = new Dictionary<string, List<IObjectName>>(); 

		private void UpdateObjName(IObjectName obj)
		{
			if (WaitName.ContainsKey(obj.Name))
			{
				var list = WaitName[obj.Name];
				foreach (var finded in list)
				{
					finded.ObjectName = obj.ObjectName;
				}
				WaitName.Remove(obj.Name);
			}
		}

		private void CreateModel()
		{
			Action<IObjectName> UpdateName = (obj) =>
																							{
																								//todo remove condition
																								if (obj == null || string.IsNullOrWhiteSpace(obj.Name))
																									return;

																								if (Structs.ContainsKey(obj.Name))
																								{
																									var refObj = Structs[obj.Name] as IObjectName;
																									if (refObj == null)
																									{
																										throw new NotImplementedException();
																									}
																									else
																									{
																										obj.ObjectName = refObj.ObjectName;
																									}
																								}
																								else
																								{
																									if (!WaitName.ContainsKey(obj.Name))
																										WaitName.Add(obj.Name, new List<IObjectName>());
																									WaitName[obj.Name].Add(obj);
																								}
																							};
			
			foreach (Expression refEx1 in _grammar.NamedPatterns.Cast<Expression>())
			{
				IStruct str = BuilderBase.Factory(refEx1, UpdateName).Fill();

				//todo fix uknown name
				if (str != null && !string.IsNullOrWhiteSpace(str.Name))
				{
					Structs.Add(str.Name, str);

					IObjectName name = str as IObjectName;
					if (name != null)
						UpdateObjName(name);
				}
/*
				if (refEx1 is ReferenceExp)
				{
					ParseReferenceExt((ReferenceExp)refEx1);
				}
				else
				{
					throw new NotImplementedException(refEx1.GetType().FullName);
				}
*/
			}
		}

/*		private void ParseReferenceExt(ReferenceExp refEx)
		{
			var name = refEx.name;
			var exp = refEx.exp;
			var isNull = refEx.IsNullable;

			if (exp is ChoiceExp)
			{
				ParseChoiceExp((ChoiceExp)exp, name, isNull);
			}
			else if (exp is ElementExp)
			{
				ParseElementExp((ElementExp)exp, name, isNull);
			}
			else
			{
				//fix remove continue
				return;
				throw new NotImplementedException(exp.GetType().FullName);
			}
		}



		public Dictionary<string, ObjectStruct> Objects = new Dictionary<string, ObjectStruct>();

		private void ParseElementExp(ElementExp exp, string name, bool isNull)
		{
			ObjectStruct obj = new ObjectStruct
													 {
														 Name = name,
													 };

			Objects.Add(obj.Name, obj);

			if (exp.exp is GroupExp)
			{
				var group = (GroupExp)exp.exp;
				foreach (Expression expression in group.children)
				{
					if (expression is AttributeExp)
					{
						GetAttribute((AttributeExp)expression, obj);
					}
					else if (expression is ChoiceExp)
					{
						var choiceExp = (ChoiceExp)expression;
						if (choiceExp.exp1 is AttributeExp && choiceExp.exp2 is Expression.EmptyExp)
						{
							choiceExp.exp1.IsNullable = true;
							GetAttribute((AttributeExp) choiceExp.exp1, obj);
						}
						else if (choiceExp.exp1 is ElementExp && choiceExp.exp2 is Expression.EmptyExp)
						{
							var elExp = ((ElementExp) choiceExp.exp1);
							var n = elExp.Name;
							//fix remove continue
							throw new NotImplementedException(name);
						}
						else if (choiceExp.exp1 is OneOrMoreExp && choiceExp.exp2 is Expression.EmptyExp)
						{
							//fix remove continue
							continue;
							throw new NotImplementedException(name);
						}
						else if (choiceExp.exp1 is ReferenceExp && choiceExp.exp2 is Expression.EmptyExp)
						{
							//fix remove continue
							continue;
							throw new NotImplementedException(name);
						}
						else
						{
							throw new NotImplementedException(choiceExp.exp1.GetType().FullName + " : " + choiceExp.exp2.GetType().FullName);
						}
					}
					else
					{
						//fix remove continue
						continue;
						throw new NotImplementedException(name);
					}
				}
			}
			else if (exp.exp is ChoiceExp)
			{
			}
			else if (exp.exp is InterleaveExp)
			{
			}
			else
			{
				throw new NotImplementedException("ParseElementExp " + exp.exp.GetType().FullName);
			}
		}*/

		#region Processed


		/*		private void ParseChoiceExp(ChoiceExp choice, string name, bool isNull)
				{
					bool condition = choice.children.All(x => x is ValueExp);
					if (!condition)
					{
						//fix remove continue
						return;
						throw new NotImplementedException();
					}

					IEnumerable<ValueExp> valueExps = choice.children.Cast<ValueExp>();
					List<string> values = valueExps.Select(x => x.value).Cast<string>().ToList();


					Enums.Add(name, new EnumStruct
					{
						Values = values,
						IsNull = isNull,
						EType = typeof(string),
					});
				}*/

/*		private static void GetAttribute(AttributeExp expression, ObjectStruct obj)
		{
			var builder = new AttributeExpBuilderBase(expression);

			var attr = builder.Exp;

			if (attr.exp is DataExp)
			{
				DataExpBuilder dataExp = new DataExpBuilder((DataExp) attr.exp);

				var systemType = dataExp.SystemType;

				if (systemType != null)
				{
					obj.Structs.Add(new AttrStruct
						                    {
							                    Name = builder.Name,
							                    IsNull = builder.IsNull,
							                    Type = systemType.FullName,
						                    });
				}
				else
				{
					return;
					throw new NotImplementedException();
				}
			}
			else if (attr.exp is ReferenceExp)
			{
				var attrRefType = ((ReferenceExp) attr.exp);
				var refName = attrRefType.name;
				obj.Structs.Add(new AttrStruct
					                    {
																Name = builder.Name,
																IsNull = builder.IsNull,
						                    Type = refName,
					                    });
			}
			else if (attr.exp is ValueExp)
			{
				//fix remove continue
				//							continue;
				var attrRefType = ((ValueExp) attr.exp);
				obj.AttrConst.Add(new ObjectStruct.Const
					                  {
															Name = builder.Name,
						                  Value = attrRefType.value.ToString(),
															IsNull = builder.IsNull,
					                  });
				//							throw new NotImplementedException(name + " : " + attrName);
			}
			else if (attr.exp is Tenuto.Grammar.ListExp)
			{
				//todo remove return
				return;
				throw new NotImplementedException();
			}
			else if (attr.exp is Tenuto.Grammar.ChoiceExp)
			{
				//todo remove return
				return;
				throw new NotImplementedException();
			}
			else if (attr.exp is Tenuto.Grammar.Expression.TextExp)
			{
				//todo remove return
				return;
				throw new NotImplementedException();
			}
			else
			{
				throw new NotImplementedException(attr.exp.GetType().FullName);
			}
		}*/

		#endregion

	}
}